package com.koron.common.core.util.qywx;

/**
 * @ClassName WechatUtil
 * @description
 * @Author zhouj
 * @Date 2022/3/9 10:43
 */

import com.alibaba.fastjson.JSONObject;
import com.koron.common.core.util.qywx.bean.ContentItem;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 企业微信工具类
 *
 * @author hury
 */
//@Slf4j
@Component
public class QywxMessageUtil {

    public static final String CHAR_SET = "utf-8";

    private CloseableHttpClient httpClient;
    private HttpPost httpPost;
    private HttpGet httpGet;

    @Autowired
    QywxMessageProperties qywxMessageProperties;

    //缓存token对象
    public static CacheTokenBean cacheTokenBean = new CacheTokenBean();

    private static Logger log = LoggerFactory.getLogger(QywxMessageUtil.class);

    /**
     * 获取access_token
     *
     * @throws IOException
     */
    public String getToken() throws IOException {
        return getToken(qywxMessageProperties.getCorpId(), qywxMessageProperties.getCorpSecret());
    }

    /**
     * 获取access_token
     *
     * @param corpid     企业ID
     * @param corpsecret 应用的凭证密钥
     * @throws IOException
     */
    public String getToken(String corpid, String corpsecret) throws IOException {
        httpClient = HttpclientUtil.createSSLClientDefault();
        httpGet = new HttpGet(qywxMessageProperties.getTokenUrl() + "?corpid=" + corpid + "&corpsecret=" + corpsecret);
        CloseableHttpResponse res = httpClient.execute(httpGet);
        String resString = "";

        try {
            HttpEntity entity = res.getEntity();
            resString = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);

            JSONObject jo = JSONObject.parseObject(resString);
            return jo.getString("access_token");
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            res.close();
        }
        log.debug("resp:{}", resString);
        return resString;
    }

    /**
     * 获取用户信息
     *
     * @throws IOException
     */
    public String getUserInfo(String code) throws IOException {
        httpClient = HttpclientUtil.createSSLClientDefault();
        httpGet = new HttpGet(qywxMessageProperties.getUserInfoUrl() + "?access_token=" + getCacheToken() + "&code=" + code);
        CloseableHttpResponse res = httpClient.execute(httpGet);
        String resString = "";

        try {
            HttpEntity entity = res.getEntity();
            resString = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);

            JSONObject jo = JSONObject.parseObject(resString);
            return jo.getString("UserId");
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            res.close();
        }
        log.debug("resp:{}", resString);
        return resString;
    }

    /**
     * 获取企业微信token
     *
     * @return
     * @throws Exception
     */
    public synchronized String getCacheToken() {

        Date currentTime = new Date();
        Date pastTime = cacheTokenBean.getCacheDate();
        if (pastTime != null) {
            long diff = currentTime.getTime() - pastTime.getTime();
            long min = diff / (1000 * 60);
            //官方2小时过期
            if (min < 100 && StringUtils.isNotEmpty(cacheTokenBean.getCacheToken())) {
                log.info("使用缓存token：{}，时间：{}", cacheTokenBean.getCacheToken(), cacheTokenBean.getCacheDate());
                return cacheTokenBean.getCacheToken();
            }
        }

        try {
            cacheTokenBean.setCacheToken(getToken());
            cacheTokenBean.setCacheDate(currentTime);

        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        log.info("刷新缓存token：{}，时间：{}", cacheTokenBean.getCacheToken(), cacheTokenBean.getCacheDate());
        return cacheTokenBean.getCacheToken();

    }

    /**
     * 发送消息
     *
     * @param data  post数据
     * @param token 鉴权token
     * @return
     * @throws IOException
     */
    public String sendMsg(String data, String token) throws IOException {
        httpClient = HttpclientUtil.createSSLClientDefault();
        httpPost = new HttpPost(qywxMessageProperties.getMessageSendUrl() + "?access_token=" + getCacheToken());
        httpPost.setEntity(new StringEntity(data, CHAR_SET));
        CloseableHttpResponse res = httpClient.execute(httpPost);
        String resString;
        try {
            HttpEntity entity = res.getEntity();
            resString = EntityUtils.toString(entity, CHAR_SET);
            EntityUtils.consume(entity);
        } finally {
            res.close();
        }
        log.debug("call [{}], param:{}, res:{}", qywxMessageProperties.getMessageSendUrl(), data, resString);
        return resString;
    }

    /**
     * 创建文本消息
     * <p>
     * 示例：
     *
     * <pre>
     * {
     * "touser" : "UserID1|UserID2|UserID3",
     * "toparty" : "PartyID1|PartyID2",
     * "totag" : "TagID1 | TagID2",
     * "msgtype" : "text",
     * "agentid" : 1,
     * "text" : {
     * "content" : "你的快递已到，请携带工卡前往邮件中心领取。\n出发前可查看<a href=\"http://work.weixin.qq.com\">邮件中心视频实况</a>，聪明避开排队。"
     * },
     * "safe":0,
     * "enable_id_trans": 0,
     * "enable_duplicate_check": 0,
     * "duplicate_check_interval": 1800
     * }
     * </pre>
     *
     * @param touser  指定接收消息的成员，成员ID列表（多个接收者用‘|’分隔，最多支持1000个）。
     *                特殊情况：指定为"@all"，则向该企业应用的全部成员发送
     * @param agentid 企业应用的id
     * @param content 消息内容，最长不超过2048个字节，超过将截断（支持id转译）
     * @return
     */
    public String createTextData(String touser, int agentid, String content) {
        Map<String, Object> data = new HashMap<String, Object>();
        Map<String, Object> text = new HashMap<String, Object>();
        data.put("touser", touser);
        data.put("msgtype", "text");
        data.put("agentid", agentid);
        text.put("content", content);
        data.put("text", text);
        return JSONObject.toJSONString(data);
    }

    /**
     * 创建文件消息
     * <p>
     * 示例：
     *
     * <pre>
     * {
     * "touser" : "UserID1|UserID2|UserID3",
     * "toparty" : "PartyID1|PartyID2",
     * "totag" : "TagID1 | TagID2",
     * "msgtype" : "file",
     * "agentid" : 1,
     * "file" : {
     * "media_id" : "1Yv-zXfHjSjU-7LH-GwtYqDGS-zz6w22KmWAT5COgP7o"
     * },
     * "safe":0,
     * "enable_duplicate_check": 0,
     * "duplicate_check_interval": 1800
     * }
     * </pre>
     *
     * @param touser   指定接收消息的成员，成员ID列表（多个接收者用‘|’分隔，最多支持1000个）。
     *                 特殊情况：指定为"@all"，则向该企业应用的全部成员发送
     * @param agentid  企业应用的id
     * @param media_id 文件id，可以调用上传临时素材接口获取
     * @return
     */
    public String createFileData(String touser, int agentid, String media_id) {
        Map<String, Object> data = new HashMap<String, Object>();
        Map<String, Object> file = new HashMap<String, Object>();
        data.put("touser", touser);
        data.put("msgtype", "file");
        data.put("agentid", agentid);
        file.put("media_id", media_id);
        data.put("file", file);

        return JSONObject.toJSONString(data);
    }

    /**
     * 创建图片消息
     * <p>
     * 示例：
     *
     * <pre>
     * {
     *    "touser" : "zhangsan|lisi",
     *    "toparty": "1|2",
     *    "totag": "1|2",
     *    "msgtype" : "miniprogram_notice",
     *    "miniprogram_notice" : {
     *         "appid": "wx123123123123123",
     *         "page": "pages/index?userid=zhangsan&orderid=123123123",
     *         "title": "会议室预订成功通知",
     *         "description": "4月27日 16:16",
     *         "emphasis_first_item": true,
     *         "content_item": [
     *             {
     *                 "key": "会议室",
     *                 "value": "402"
     *             },
     *             {
     *                 "key": "会议地点",
     *                 "value": "广州TIT-402会议室"
     *             },
     *             {
     *                 "key": "会议时间",
     *                 "value": "2018年8月1日 09:00-09:30"
     *             },
     *             {
     *                 "key": "参与人员",
     *                 "value": "周剑轩"
     *             }
     *         ]
     *     },
     *    "enable_id_trans": 0,
     *    "enable_duplicate_check": 0
     * }
     * </pre>
     *
     * @param touser              指定接收消息的成员，成员ID列表（多个接收者用‘|’分隔，最多支持1000个）。
     *                            特殊情况：指定为"@all"，则向该企业应用的全部成员发送
     * @param appid               小程序appid，必须是与当前小程序应用关联的小程序
     * @param page                点击消息卡片后的小程序页面，仅限本小程序内的页面。该字段不填则消息点击后不跳转。
     * @param title               消息标题，长度限制4-12个汉字。
     * @param description         消息描述，长度限制4-12个汉字。
     * @return
     */
    public String createMiniprogramData(String touser, String appid, String page, String title, String description, List<ContentItem> contentItems) {
        Map<String, Object> data = new HashMap<String, Object>();
        Map<String, Object> miniprogram = new HashMap<String, Object>();
        data.put("touser", touser);
        data.put("msgtype", "miniprogram_notice");
        data.put("enable_id_trans", 0);
        data.put("enable_duplicate_check", 0);

        miniprogram.put("appid", appid);
        miniprogram.put("page", page);
        miniprogram.put("title", title);
        miniprogram.put("description", description);
        miniprogram.put("emphasis_first_item", true);
        miniprogram.put("contentItem", contentItems);

        data.put("miniprogram_notice", miniprogram);

        return JSONObject.toJSONString(data);
    }

    /**
     * 创建小程序通知消息
     * <p>
     * 示例：
     *
     * <pre>
     * {
     * "touser" : "UserID1|UserID2|UserID3",
     * "toparty" : "PartyID1|PartyID2",
     * "totag" : "TagID1 | TagID2",
     * "msgtype" : "image",
     * "agentid" : 1,
     * "image" : {
     * "media_id" : "MEDIA_ID"
     * },
     * "safe":0,
     * "enable_duplicate_check": 0,
     * "duplicate_check_interval": 1800
     * }
     * </pre>
     *
     * @param touser   指定接收消息的成员，成员ID列表（多个接收者用‘|’分隔，最多支持1000个）。
     *                 特殊情况：指定为"@all"，则向该企业应用的全部成员发送
     * @param agentid  企业应用的id
     * @param media_id 图片媒体文件id，可以调用上传临时素材接口获取
     * @return
     */
    public String createImageData(String touser, int agentid, String media_id) {
        Map<String, Object> data = new HashMap<String, Object>();
        Map<String, Object> image = new HashMap<String, Object>();
        data.put("touser", touser);
        data.put("msgtype", "image");
        data.put("agentid", agentid);
        image.put("media_id", media_id);
        data.put("image", image);

        return JSONObject.toJSONString(data);
    }

    /**
     * 上传临时素材
     *
     * <pre>
     * 上传的媒体文件限制
     * 所有文件size必须大于5个字节
     *
     * 图片（image）：10MB，支持JPG,PNG格式
     * 语音（voice） ：2MB，播放长度不超过60s，仅支持AMR格式
     * 视频（video） ：10MB，支持MP4格式
     * 普通文件（file）：20MB
     * </pre>
     *
     * @param fileType 媒体文件类型，分别有图片（image）、语音（voice）、视频（video），普通文件（file）
     * @param filePath 文件路径
     * @param token    调用接口凭证
     * @return media_id 媒体文件上传后获取的唯一标识，3天内有效
     * @throws Exception
     */
    public String upload(String fileType, String filePath, String token) throws Exception {
        // 返回结果
        String result = null;
        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            throw new IOException("文件不存在");
        }

        String uploadUrl = qywxMessageProperties.getMediaUploadUrl() + "?access_token=" + token + "&type=" + fileType;
        URL url = new URL(uploadUrl);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setRequestMethod("POST");// 以POST方式提交表单
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);// POST方式不能使用缓存
        // 设置请求头信息
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Charset", "UTF-8");
        // 设置边界
        String BOUNDARY = "----------" + System.currentTimeMillis();
        conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        // 请求正文信息
        // 第一部分
        StringBuilder sb = new StringBuilder();
        sb.append("--");// 必须多两条道
        sb.append(BOUNDARY);
        sb.append("\r\n");
        sb.append("Content-Disposition: form-data;name=\"media\"; filename=\"" + file.getName() + "\"\r\n");
        sb.append("Content-Type:application/octet-stream\r\n\r\n");

        // 获得输出流
        OutputStream out = new DataOutputStream(conn.getOutputStream());
        // 输出表头
        out.write(sb.toString().getBytes("UTF-8"));
        // 文件正文部分
        // 把文件以流的方式 推送道URL中
        DataInputStream din = new DataInputStream(new FileInputStream(file));
        int bytes = 0;
        byte[] buffer = new byte[1024];
        while ((bytes = din.read(buffer)) != -1) {
            out.write(buffer, 0, bytes);
        }
        din.close();
        // 结尾部分
        byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");// 定义数据最后分割线
        out.write(foot);
        out.flush();
        out.close();
        if (HttpsURLConnection.HTTP_OK == conn.getResponseCode()) {

            StringBuffer strbuffer = null;
            BufferedReader reader = null;
            try {
                strbuffer = new StringBuffer();
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String lineString = null;
                while ((lineString = reader.readLine()) != null) {
                    strbuffer.append(lineString);
                }
                if (result == null) {
                    result = strbuffer.toString();
                }
            } catch (IOException e) {
                log.error(e.getMessage(),e);
            } finally {
                if (reader != null) {
                    reader.close();
                }
            }
        }
        JSONObject jsonObject = JSONObject.parseObject(result);
        return jsonObject.getString("media_id");
    }


    /**
     * 发送文本
     *
     * @param content 文本内容
     */
    public String sendText(String touser, String content) throws IOException {
        String res = null;
        String token = getCacheToken();
        String data = createTextData(touser, qywxMessageProperties.getAgentId(), content);
        res = sendMsg(data, token);
        log.info("企微消息请求data  >>>" + data);
        log.info("企微消息响应res   >>>" + res);
        return res;
    }

    /**
     * 发送文件
     *
     * @param filePath 文件路径
     */
    public void sendFile(String filePath, String touser) {
        try {
            String token = getCacheToken();
            String media_id = upload("file", filePath, token);
            String data = createFileData(touser, qywxMessageProperties.getAgentId(), media_id);
            String res = sendMsg(data, token);
            log.info("data  >>>" + data);
            log.info("res   >>>" + res);
        } catch (Exception e) {
            e.getStackTrace();
        }
    }

    /**
     * 发送图片
     *
     * @param filePath 图片路径
     */
    public void sendImage(String filePath, String touser) {
        QywxMessageUtil wx = new QywxMessageUtil();
        try {
            String token = getCacheToken();
            String media_id = upload("image", filePath, token);
            String data = createImageData(touser, qywxMessageProperties.getAgentId(), media_id);
            String res = sendMsg(data, token);
            log.info("data  >>>" + data);
            log.info("res   >>>" + res);
        } catch (Exception e) {
            e.getStackTrace();
        }
    }

    /**
     * 发送图片
     */
    public void sendMiniprogram(String touser, String page, String title, String description, List<ContentItem> contentItemList) {
        try {
            String token = getCacheToken();
            String data = createMiniprogramData(touser, qywxMessageProperties.getAppId(), page, title, description, contentItemList);
            String res = sendMsg(data, token);
            log.info("data  >>>" + data);
            log.info("res   >>>" + res);
        } catch (Exception e) {
            e.getStackTrace();
        }
    }

}
